Replace op.Constant float scalars with Python literals for dtype-safe auto-casting - #58
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes ONNX mixed-dtype load errors when adding a scalar 1.0 to tensors that may become BFLOAT16 after weight application (notably in OffsetRMSNorm and Gemma3n AltUp coefficient correction), by explicitly casting the scalar Constant to match the target tensor dtype.
Changes:
- Cast the scalar
1.0Constant viaop.CastLike(..., weight_tensor)beforeop.AddinOffsetRMSNorm. - Apply the same cast-before-add pattern to Gemma3n AltUp correction coefficients.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/mobius/components/_rms_norm.py | Prevents dtype mismatch in OffsetRMSNorm by casting the scalar constant to the weight’s dtype before Add. |
| src/mobius/models/gemma3n.py | Prevents dtype mismatch in Gemma3n AltUp coefficient correction by casting the scalar constant to the coefficients’ dtype before Add. |
🏗️ Architecture Diff
No architecture changes detected. ✅ Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed) |
justinchuby
force-pushed
the
justinchu/fix-castlike-dtype
branch
from
March 28, 2026 14:48
a931ea8 to
67a3842
Compare
justinchuby
marked this pull request as draft
March 28, 2026 15:46
Use CastLike to cast the float constant 1.0 to match the weight/coefs dtype before Add. Without this, op.Add(bf16_tensor, 1.0) creates a type mismatch since 1.0 becomes a float32 Constant. Note: bf16 inference still fails on ORT CPU EP (no bf16 kernels) and on CUDA EP due to ORT Attention op decomposition creating mixed-type nodes internally. This is an ORT limitation, not a graph construction issue. f16 works on both CPU and CUDA. Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
Ensure float32 literals are cast to match model-precision tensors before use in arithmetic ops. Affected sites: - _activations.py: quick_gelu scalar 1.702 - _diffusion.py: AdaLayerNormZero constant 1.0 added to scale - _moe.py: threshold and neg_inf constants matched to scores dtype (x2) - gemma3n.py: router_input_scale constant; sqrt(2) divisor in Laurel residual - _ssm.py: epsilon constant in RMS variance computation - _audio.py: 0.5 half-scaling constant in ConformerBlock Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
justinchuby
force-pushed
the
justinchu/fix-castlike-dtype
branch
from
April 1, 2026 16:25
a440239 to
4d56eca
Compare
Tests build OffsetRMSNorm, quick_gelu, and AdaLayerNormOutput with float16/bfloat16 inputs and assert CastLike ops are present. Verified tests FAIL without the CastLike fix and PASS with it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
onnxscript auto-casts Python scalars (int/float/bool) to match the dtype of the other operand in binary ops. op.Constant(value_float=x) returns ir.Value(FLOAT) which is NOT auto-cast, causing dtype mismatches in BF16/FP16 builds. Changes: - _activations.py: 1.702 → Python literal (op.Mul auto-casts) - _audio.py: 0.5 → inline literals (removes CastLike + intermediate var) - _diffusion.py: 1.0 → Python literal (op.Add auto-casts) - _moe.py: 1e-9, routed_scaling_factor, 2*jitter_eps → Python literals; -1e30 uses op.CastLike(-1e30, scores) with Python literal to avoid cache-key collision (Expand has no type-variable binding for its input) - _rms_norm.py: 1.0 → Python literal (op.Add auto-casts) - gemma3n.py: router_input_scale, 1.0, sqrt(2) → Python literals Test update: TestCastLikeDtypeSafety → TestPythonLiteralAutocast. New tests call _cast_module_dtype to simulate production dtype promotion, then verify output dtype matches input dtype with no CastLike ops present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
Performance Comparison
|
justinchuby
marked this pull request as ready for review
April 1, 2026 18:42
Add 10 new parametrized tests covering the 5 components identified by the code reviewer as missing BF16/FP16 coverage: SigmoidTopKGate (2 tests x 2 dtypes = 4): - test_routing_weights_dtype: verifies 1e-9 epsilon in op.Add auto-casts so routing weights stay in FP16/BF16 (not widened to FP32) - test_routed_scaling_factor_autocasts: verifies routed_scaling_factor Python float literal in op.Mul auto-casts to routing dtype SparseMixerGate (1 test x 2 dtypes = 2): - test_castlike_neg_inf_uses_input_dtype: verifies op.CastLike(-1e30, scores) correctly casts the -1e30 constant to the input dtype for use in op.Where and op.Expand, preventing FP32 type mismatches ConformerEncoderLayer (1 test x 2 dtypes = 2): - test_macaron_half_weight_autocasts: verifies 0.5 Macaron weight literals in op.Mul auto-cast to hidden state dtype (FP16/BF16) Gemma3nAltUp (1 test x 2 dtypes = 2): - test_router_input_scale_autocasts: verifies router_input_scale (hidden_size**-1.0 Python float) auto-casts in op.Mul during _compute_router_modalities All 17 tests pass (10 new + 7 existing). Signed-off-by: Justin Chu <justinchuby@users.noreply.github.com>
gramalingam
approved these changes
Apr 13, 2026
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
gramalingam
added a commit
that referenced
this pull request
Apr 13, 2026
…rals Replace all 68 instances of op.Constant(value_float=X) across 30 files with plain Python float literals or float() expressions. This leverages onnxscript's auto-casting of Python scalars to match the dtype of the other operand in binary ops, which fixes dtype mismatches when models use bfloat16 or float16. Patterns replaced: - op.Constant(value_float=X) -> X (plain literal) - op.Constant(value_float=float(expr)) -> float(expr) - op.Constant(value_float=self.attr) -> self.attr - op.CastLike(op.Constant(value_float=X), ref) -> op.CastLike(X, ref) Note: op.Constant(value_int=X) replacements were NOT included because onnxscript creates initializers (not Constant nodes) for Python int literals, causing 'already registered' collisions when the same int value appears multiple times in a graph. This is an onnxscript limitation that needs to be resolved upstream before value_int cleanup can proceed. Continuation of PR #58 which established this pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: G Ramalingam <grama@microsoft.com>
gramalingam
added a commit
that referenced
this pull request
Apr 13, 2026
…rals Replace all 68 instances of op.Constant(value_float=X) across 30 files with plain Python float literals or float() expressions. This leverages onnxscript's auto-casting of Python scalars to match the dtype of the other operand in binary ops, which fixes dtype mismatches when models use bfloat16 or float16. Patterns replaced: - op.Constant(value_float=X) -> X (plain literal) - op.Constant(value_float=float(expr)) -> float(expr) - op.Constant(value_float=self.attr) -> self.attr - op.CastLike(op.Constant(value_float=X), ref) -> op.CastLike(X, ref) Note: op.Constant(value_int=X) replacements were NOT included because onnxscript creates initializers (not Constant nodes) for Python int literals, causing 'already registered' collisions when the same int value appears multiple times in a graph. This is an onnxscript limitation that needs to be resolved upstream before value_int cleanup can proceed. Continuation of PR #58 which established this pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: G Ramalingam <grama@microsoft.com>
gramalingam
added a commit
that referenced
this pull request
Apr 14, 2026
…autocast (#166) Continuation of PR #58 which established this pattern. Replace all 68 instances of op.Constant(value_float=X) across 30 files with plain Python float literals or float() expressions. This leverages onnxscript's auto-casting of Python scalars to match the dtype of the other operand in binary ops, which fixes dtype mismatches when models use bfloat16 or float16. Patterns replaced: - op.Constant(value_float=X) -> X (plain literal) - op.Constant(value_float=float(expr)) -> float(expr) - op.Constant(value_float=self.attr) -> self.attr - op.CastLike(op.Constant(value_float=X), ref) -> op.CastLike(X, ref) Note: This is part 1. (A similar change can be done for int/ints/floats, but will do that in separate PRs.) --------- Signed-off-by: G Ramalingam <grama@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
op.Constant(value_float=1.0)always creates a float32 ONNX Constant node. When the surrounding computation uses bfloat16 or float16 tensors, this introduces a dtype mismatch — ONNX requires both operands to have the same type.Solution
onnxscript auto-casts Python scalars (
int,float,bool) to match the dtype of the other operand in binary ops. Soop.Add(bf16_tensor, 1.0)works correctly — the1.0is auto-cast to bf16.This PR replaces
op.Constant(value_float=...)with plain Python literals wherever possible. In two places where auto-cast cannot help (neg_infvalues passed toop.Expandwhich has no typed operand to infer from),op.CastLike(-1e30, scores)is used instead.Changes
_audio.pyop.Constant(value_float=0.5)0.5(Python literal)_diffusion.pyop.Constant(value_float=1.0)→onevariable1.0inline_moe.py(SigmoidTopKGate)op.CastLike(op.Constant(...), ...)for eps, scale1e-9,self.routed_scaling_factor(Python literals)_moe.py(SparseMixerGate)op.Constant(value_float=...)for threshold, neg_inf2.0 * jitter_eps(literal);op.CastLike(-1e30, scores)for neg_infgemma3n.py(router)op.Constant(value_float=self.router_input_scale)self.router_input_scale(Python float)gemma3n.py(Laurel residual)op.Constant(value_float=float(math.sqrt(2)))float(math.sqrt(2))(Python literal)Testing
New test file:
src/mobius/components/_castlike_dtype_test.pyOffsetRMSNorm,quick_gelu, andAdaLayerNormOutputNote
bfloat16 end-to-end inference still fails on ORT CPU EP (no bf16 kernels) and on ORT CUDA EP (Attention op decomposition creates mixed-type nodes internally) — these are ORT runtime limitations, not graph construction issues.